ทดลองเขียน Edge/Chrome extension สำหรับอ่าน comment ใน Facebook live แบบไม่ใช้ API
Table of Contents
พอดีวันนี้เป็นวันหยุดชดเชย ผมก็ไถ facebook feed ไปเรื่อยๆก็สังเกตได้ว่าบ้านเรานี่ live ขายของกันเยอะจริงๆ แถมหาวิธีสื่อสารในการขายกันได้สมบูรณ์ด้วยทั้งๆที่ facebook เองก็ไม่ได้ให้เครื่องมืออะไรนอกจากช่อง comment แต่แทบทุกคนที่ขายจะต้องมีทีมงานหลังบ้านคอยนับจำนวนคน cf หรือคนตอบลูกค้าอยู่ ผมเลยนึกเล่นๆว่ามันจะมีวิธีทำให้พ่อค้าแม่ค้าสามารถปิดการขายใน live ด้วยตัวคนเดียวได้ไหม
ปัญหา #
พ่อค้าแม่ค้าจะ live ถ้ามีลูกค้าจำนวนมากจะเกิดปัญหาอ่าน comment ไม่ทัน จดชื่อลูกค้าไม่ทัน อยากขายไปด้วยเก็บข้่อมูลไปด้วยคนเดียวดูไม่มีทางเป็นไปได้แน่นอน จำเป็นต้องหาคนหรือจ้างพนักงานมาช่วย
แนวทางแก้ปัญหา #
เขียน extension เพื่อช่วยรับ comment ใหม่ๆเข้ามาแล้ว พิจารณาด้วย regular expression ว่า message ที่ถูกพิมพ์ส่งมาตรงกับเงื่อนไขของพ่อค้าแม่ค้าไหม
สร้าง Extension กัน #
อ่านคู่มือก่อนทำอย่างอื่น!! #
ก่อนจะไปถึงจุดอื่นๆในบทความนี้ผมอยากให้ทุกๆท่านอ่านพื้นฐานการสร้าง extension สำหรับ browser กันก่อนนะครับจะได้เห็นภาพคร่าวๆกันก่อน
เริ่มต้นจากความว่างเปล่า #
- สร้าง
index.js
แล้วเขียน function ง่ายๆทิ้งไว้ก่อน
const hello = () => {
console.log('Hello Edge');
}
hello();
- สร้าง
manifest.json
เพื่อกำหนดค่าพื้นฐานของ extension ตั้งชื่อ กำหนด match url ด้วยนะ
{
"name": "Facebook Live Helper",
"version": "0.1",
"description": "Check customer name and price",
"content_scripts": [
{
"matches": [
"https://www.facebook.com/*/videos/*"
],
"js": [
"index.js"
]
}
],
"manifest_version": 2
}

ติดตั้ง Extension ใน browser แบบไม่ต้องผ่าน store #
- ไป settings / extension

- เปิด developer

- load unpacked เลือก folder ของ extension เราซะ


- ทดลองเข้า url เปิด live สักคลิปที่มี url match ของ
manifest.json
แบบนี้"https://www.facebook.com/*/videos/*"
แล้วดูว่าใน console มีHello Edge
อยู่ไหม

พิจารณาหน้า page ของ Facebook Live เพื่อหาว่าข้อมูลเรามาจากตรงไหน #

- ส่วนที่เราสนใจจะอยู่แค่ในกรอบสีแดง(ภาพด้านบน) เพราะนั่นคือส่วนของ comment ที่มีการ update ตลอดเวลา
- ในกรอบแดงจะประกอบด้วย
<div>
ที่มี class ชื่อว่าUFICommentActorAndBodySpacing
ซึ่งจะครอบชื่อและข้อความของ comment เอาไว้ - ชื่อของผู้คอมเมนต์จะเป็น
<a>
ที่มี class ชื่อว่าUFICommentActorName
- comment จะอยู่ใน
<span>
ที่มี class ชื่อว่าUFICommentBody
- ส่วน pinned comment ให้สังเกตว่าจะมี
<span>
ที่มี class ชื่อว่าUFILivePinnedCommentLabel
แปะอยู่ด้านบน - วิธีดึงข้อมูลของเราก็คือจะวน loop ดึงเอาข้อมูลจาก
.UFICommentActorAndBodySpacing
ทุกๆ 0.5 วินาที โดยเราจะเลือกเอาตัวที่อยู่ล่างสุดหรือตัวสุดท้ายใน NodeList เพราะมันคือข้อความล่าสุด แต่หากมี pinned comment เราจะเลือกตัวรองสุดท้ายแทน

ได้เวลาลงไม้ลงมือกันล่ะครับ #
- เนื่องจาก facebook ใช้ javascript render ตัว element ทำให้ element บางตัวจะถูกแสดงขึ้นมาหลังจาก load เสร็จดังนั้นเราต้องเขียน function ที่จะรอทำงานก็ต่อเมื่อมี element ที่เราต้องการปรากฏขึ้นมาก่อน เพื่อไม่ให้เสียเวลาผมก็จะขอหยิบเอา function
elementReady
ของคุณ Joshua Wilson มาใช้เลยละกัน
Gist ของ function elementReady #
jwilson8767/es6-element-ready.js
เมื่อเข้าใจที่มาที่ไปแล้วเราก็หยิบเอา function แปะลงไปที่ index.js
เลย
const elementReady = (selector) => {
return new Promise((resolve, reject) => {
let el = document.querySelector(selector);
if (el) {resolve(el);}
new MutationObserver((mutationRecords, observer) => {
// Query for elements matching the specified selector
Array.from(document.querySelectorAll(selector)).forEach((element) => {
resolve(element);
//Once we have resolved we don't need the observer anymore.
observer.disconnect();
});
})
.observe(document.documentElement, {
childList: true,
subtree: true
});
});
}
- เขียน function ไล่จับ comment โดยผมก็จะไล่อ่านไปตาม node ที่มีชื่อ class ตามที่ผมพูดถึงในหัวข้อก่อนหน้า ส่วนนี้ผมจะขอเขียนเนื้อหาใน comment ของ code แทนนะครับ
let commentSet = new Set();
let temporarySize = 0;
const commentReader = () => {
//Pinned comment node
const pinnedNodes = document.querySelectorAll('.UFILivePinnedCommentLabel');
//Comment nodes
const commentNodes = document.querySelectorAll('.UFICommentActorAndBodySpacing');
//ถ้ามี pinned ให้ถอยมาพิจารณา comment รองสุดท้าย แต่ถ้าไม่มีก็เอาอันสุดท้ายเลย
const index = (pinnedNodes.length > 0 ? commentNodes.length - 2 : commentNodes.length - 1);
//ถ้า index แล้วก็เรียก comment node ออกมา
const commentNode = commentNodes[index];
//ดึง node ของชื่อและข้อความใน comment
const customerNameNode = commentNode.querySelector('.UFICommentActorName');
const commentMessageNode = commentNode.querySelector('.UFICommentBody');
if (customerNameNode) {
//ดึงข้อมูลออกมาด้วย innerText และ href ในกรณีที่เป็น facebook profile url
const name = customerNameNode.innerText;
const url = customerNameNode.href;
const message = commentMessageNode.innerText;
//เก็บไว้ใน comment object
const comment = { name, message, url }
//พิจารณาว่า message มันมีตัวเลขอยู่ไหม ถ้ามีถือว่าลูกค้าสนใจ
//ตรงนี้แก้ไขไปตามเงื่อนไขที่ต้องการได้เลย
if (/\d+/.test(message.toLowerCase())) {
//บันทึก comment object ลงใน set เพราะมันจัดการข้อมูลซ้ำให้ได้เองในตัว
//แต่ต้องเปลี่ยนมันเป็น string กันก่อน ตอนนี้ก็ถือว่าเป็น Set ของ string แล้ว
commentSet.add(JSON.stringify(comment));
//add ใส่ set ไปแล้วจำนวนมันมากขึ้นจากเดิมไหม ถ้ามากขึ้นแปลว่ามี comment ใหม่เข้ามา
if (commentSet.size > temporarySize) {
//ปรับจำนวนของ size ว่าเปลี่ยนไปแล้วนะ จะได้เอาไว้เทียบกับอีก 500ms ต่อไป
temporarySize = commentSet.size;
//เรียบร้อยแล้วก็ log ออกมาดูหน่อยว่าได้ผลไหม แต่อันนี้ขอ censor หน่อยเผื่อตอนทำ screenshot จะได้ไม่โดนฟ้อง :3
console.log({
name: `${comment.name.substring(0,4)}...(censor)`,
profileUrl: `${comment.url.substring(0,44)}...(censor)`,
message: comment.message,
})
}
}
}
// วนใหม่อีกใน 0.5 วินาที
setTimeout(commentReader, 500);
}
สรุปผลการทดลอง #

- ใน console ก็จะแสดงรายชื่อ profile url พร้อมกับ message ที่เรา filter แล้วอย่างสวยงาม
- ถึงแม้จะเห็นผลดี แต่ยังไม่เคยลองกับ page ใหญ่ๆที่มี comment เข้ามารัวๆตรงนี้ก็ยังการันตีไม่ได้ชัดเจนนักว่าจะตกหล่นไหม อาจจะปรับรอบการวนให้ถี่ขึ้นได้ แต่ก็ได้ข้อความตามที่แสดงขึ้นมาใหม่ในหน้า web เท่านั้น ไม่รู้ว่า facebook เองจะส่งมาให้เราทั้งหมดหรือเปล่า
- ต้องเปิดตั้งแต่เริ่ม live เพราะถ้าเปิดทีหลัง คนก่อนหน้าก็จะตกหล่น
- ถ้าเน็ตหลุดหรือเผลอ refresh ตัวนี้จะนับใหม่เลย ข้อมูลก็หายไป
- ไม่สามารถดึงข้อมูลก่อนหน้ามาได้
แนวทางการพัฒนาต่อ #
- เขียน function ดึง comment บนๆมาด้วยตอนที่โหลดครั้งแรก
- save เป็นไฟล์ออกมาก่อนได้ ป้องกันปัญหาเวลาเน็ตหลุดหรือเผลอปิด
- เพิ่ม field สำหรับกำหนด pattern ของ message เพราะการขายแต่ละอย่างจะเปลี่ยนตามราคาหรือชื่อไปเรื่ือยๆ
- ถ้ามี stock ก็ใส่จำนวนแล้วค่อยๆหัก stock ไปก็ได้
- เปลี่ยนไปใช้ Facebook Live Comments API
ตัวอย่าง code #
Github: facebook-live-helper-for-edge
จบไปแล้วครับสำหรับการทดลองง่ายๆในวันนี้ เชื่อว่าหลายๆท่านน่าจะเห็นช่องทางในการพัฒนาต่อได้นะครับ สำหรับท่านไหนที่มีข้อเสนอแนะสามารถบอกผมได้เลยครับ ผมยินดีมากที่จะได้ปรับปรุงครับ สำหรับท่านใดที่มีคำถามสามารถสอบถามเข้ามาที่ Inbox ของ Facebook Page ได้เลยนะครับ